//
//  AppDelegate.m
//  RFIXiOSDemo
//
//  Created by bill bai on 2025/1/21.
//

#import "AppDelegate.h"

#pragma mark - RFIX SDK & RDelivery SDK Headers
// +++++++ 引入 RFIX SDK 和 RDelivery SDK 头文件 +++++++
#import <RFIX/RFIX.h>
#import <RFIX/RFIXForTest.h>

#import <RDelivery/RDLoggerImpl.h>
#import <RDelivery/RDMMKVFactoryImpl.h>
#import <RDelivery/RDNetworkImpl.h>
#import <RDelivery/RDelivery.h>
#import <RDelivery/RDeliveryJsonModelImpl.h>

// import 自己实现的 RDelivery Logger 依赖注入对象，用于设置 RDelivery SDK 的日志输出方式 （详见下文）
#import "RFDemoRDeliveryLogger.h"
// +++++++++++++++++++++++++++++++++++++++++++++++++++

#import "RFDemoOneViewController.h"
#import "RFDemoTwoViewController.h"
#import "RFDemoInfoViewController.h"
#import "RFLicensedDebugSettingViewController.h"

void MyLogFunc(RFIXLoggerLevel level, const char *log) {
  NSLog(@"[RFIX] %s", log);
}

int MyGlobalVar = 42;

#pragma mark - AppDelegate
@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // ++++++ 初始化 RFIX SDK 和 RDelivery SDK ++++++
  // 尽量早的初始化 RFIX SDK 和 RDelivery SDK。
  // RFIXSDKStart 会尝试同步加载本地已经下载到的补丁包。并向 Shiply 后台请求补丁任务。
  // 因此，尽量在 App 启动的早期阶段初始化 RFIX SDK 和 RDelivery SDK。
  // RFIX 无法修复 RFIXSDKStart 之前的代码的问题。
  [self initRDelivery];
  
  [self setupViewControllers];
  
  [self simulateUserIdChange];
  return YES;
}

# pragma mark - Init RDelivery
- (void)initRDelivery {
  // ⚠️ ！！！！特别注意！！！！
  // App 接入和使用 RFIX SDK 的相关代码中，包括但不限于类名、方法名、日志等，*不要*出现任何与热修复相关的关键词(HotFix, Patch, HotPatch 等等)。
  // 避免 App 审核风险。
  
  // ++++++ 设置 RFIX SDK 的日志回调 ++++++
  // 设置 RFIX SDK 的日志回调
  RFIXSetLogCallback(^(RFIXLoggerLevel logLevel, NSString *_Nonnull logMessage) {
    // 这里可以替换为自己的日志输出方式
    MyLogFunc(logLevel, logMessage.UTF8String);
  });

  // ++++++ (可选) 是否启用调试模式 ++++++++
  NSNumber *isLicensedDebugModeNumber = [[NSUserDefaults standardUserDefaults] objectForKey:kRFIsLicensedDebugModeKey];
  BOOL isLicensedDebugMode = isLicensedDebugModeNumber.boolValue;
  if (isLicensedDebugMode) {
    RFIXEnableLicensedDebugMode();
    NSLog(@"RFIXSetIsLicensedDebugMode %d", isLicensedDebugMode);
  }
  
  // ++++++ 注册补丁中可能用到的 C 函数/C全局变量 ++++++
  // 补丁代码可以调用任意 ObjC 方法。但是只能调用注册到 RFIX SDK 的 C 函数和全局变量。
  // 因此，需要在 App 发布时，将常用的 C 函数和全局变量注册到 RFIX SDK。
  // 大部分系统常用 C 方法，RFIX SDK 已经内置，不需要重复注册（可参考相关文档查看已注册的方法）。
  // 但是 App 自定义的 C 函数和全局变量，需要手动注册，否则无法再补丁中调用。
  // 通常，App 需要将自己的 log 函数等注册到 RFIX SDK。
  RFIXSymbolItem gRFIXSymbolItems[] = {
    // 使用 RFIX_SYM_FUNC 注册 C 函数
    RFIX_SYM_FUNC(MyLogFunc),
    // ......
    // 使用 RFIX_SYM_VAR 注册全局变量
    RFIX_SYM_VAR(MyGlobalVar),
    // ......
  };
  RFIXRegisterSymbols(gRFIXSymbolItems, sizeof(gRFIXSymbolItems)/sizeof(gRFIXSymbolItems[0]));
  
  // ++++++ 准备 RFIX SDK 相关信息 ++++++
  // App 版本号：
  // 需要和补丁包中的 Version.json 中的 App 版本号，以及 Shiply 热修任务中配置的 App 版本号完全一致。
  // 完全一致是指字符串相等，RFIX SDK 和 Shiply 平台不会解析版本号的含义，只是原封不动的传递给 Shiply，用于匹配补丁任务。
  const char *appVersion = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] UTF8String];

  // iOS 系统版号
  // Shiply 平台创建的热修任务，可以指定 iOS 系统版本范围。
  // 这个参数会传递给 Shiply 平台，用于匹配补丁任务。
  const char *sysVersion = [UIDevice currentDevice].systemVersion.UTF8String;
  
  // 这里填入 Shiply 平台对应产品的 AppID 和 AppKey
//  const char *appID = "d2039cd4ef";
//  const char *appKey = "6960918c-1f40-4d06-938f-0ca234e849a8";
    const char *appID = "73f6cd2ec6";
    const char *appKey = "462e1668-76f4-4bd1-8ff7-693e9a449ab0";
  
  // 用户 ID
  // 用于测试验证阶段，针对特定用户白名单下发补丁包。
  // 可以根据实际情况填入自己 App 体系的用户 ID，例如 QQ 号，IMEI 等。
  // RFIX SDK 不会理解这个 ID 的含义，只是原封不动的传递给 RFIX 平台，用于匹配补丁包的用户白名单。
  // 如果 App 启动阶段还没有用户 ID，可以传入空字符串 "" （不要传 NULL）。
  // 后续用户登录后，可以调用 RFIXSDKUpdateUid 更新用户 ID （参考 [self simulateUserIdChange] 方法中的代码）
  // 如果 App 体系没有用户 ID 的概念，可以传入 Device ID 等，只要能唯一标识用户即可，可以用于测试验证阶段进行白名单下发即可。
  const char *uid = "51666";
  
  // 设备 ID。
  // 用于标识设备的唯一 ID，可以使用设备的 IMEI，IDFA 等。
  // RFIX SDK 不会理解这个 ID 的含义，只是原封不动的传递给 RFIX 平台，用于统计用量。
  const char *deviceID = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] UTF8String];
  
  // ++++++ 第三步：构造 RDeliveryDepends 依赖注入对象 ++++++
  // RDelivery SDK 本身有一些依赖，需要通过依赖注入的方式，设置给 RDelivery SDK。
  // 构造 RDeliveryDepends 依赖注入对象
  RDeliveryDepends *depends = [RDeliveryDepends new];
  // RDelivery 提供了默认的 http、kv、jsonModel 依赖实现，可以直接使用。（也可以自行实现）
  depends.httpImpl = [RDNetworkImpl sharedInstance];
  depends.kvImpl = [RDMMKVFactoryImpl sharedInstance];
  depends.jsonModelImpl = [RDeliveryJsonModelImpl sharedInstance];
  // 通过依赖注入的方式，设置 RDelivery SDK 的日志回调。
  // 虽然 RDelivery 提供了默认的日志 Logger 实现，但是它通过 NSLog 输出的.
  // 建议自行实现一个 logger。
  depends.logImpl = [RFDemoRDeliveryLogger sharedInstance];
  
  
  // ++++++ 第四步：启动 RFIX SDK ++++++
  RFIXSDKStart(appID,
               appKey,
               // 这里固定填入 RFIXEnvironmentProduction 即可。不要使用 RFIXEnvironmentTest。
               RFIXEnvironmentProduction,
               uid,
               deviceID,
               appVersion,
               sysVersion,
               depends);
  
  // ++++++++ (可选) 如果启用调试模式，则调用对应接口请求和加载调试任务补丁 +++++++
  if (RFIXLicensedDebugModeIsEnabled()) {
    // get from user default
    NSString *taskID = [[NSUserDefaults standardUserDefaults] objectForKey:kRFLicensedDebugTaskIDKey];
    if ([taskID isKindOfClass:[NSString class]] && taskID.length > 0) {
      NSLog(@"RequestAndLoadLicensedDebugTask %@", taskID);
      RFIXRequestAndLoadLicensedDebugTask(taskID, ^(BOOL success, NSError *error) {
        NSLog(@"RequestAndLoadLicensedDebugTask success %d, error %@", success, error);
      });
    } else {
      NSLog(@"No Licensed Debug Task, Skip");
    }
  }
}

- (void)simulateUserIdChange {
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 更新用户 ID
    RFIXSDKUpdateUid("56111");
  });
}

- (void)setupViewControllers {
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  self.window.backgroundColor = [UIColor whiteColor];
  [self.window makeKeyAndVisible];
  
  UITabBarController *tabbarController = [[UITabBarController alloc] init];
  
  RFDemoOneViewController *demoOneVc = [[RFDemoOneViewController alloc] initWithStyle:UITableViewStylePlain];
  UINavigationController *demoOneNavVc = [[UINavigationController alloc] initWithRootViewController:demoOneVc];
  
  RFDemoTwoViewController *demoTwoVc = [[RFDemoTwoViewController alloc] initWithCollectionViewLayout:[[UICollectionViewFlowLayout alloc] init]];
  UINavigationController *demoTwoNavVc = [[UINavigationController alloc] initWithRootViewController:demoTwoVc];
  
  RFDemoInfoViewController *demoInfoVc = [[RFDemoInfoViewController alloc] initWithStyle:UITableViewStylePlain];
  UINavigationController *demoInfoNavVc = [[UINavigationController alloc] initWithRootViewController:demoInfoVc];
  
  RFLicensedDebugSettingViewController *licensedDebugSettingVc = [[RFLicensedDebugSettingViewController alloc] init];
  UINavigationController *licensedDebugSettingNavVc = [[UINavigationController alloc] initWithRootViewController:licensedDebugSettingVc];
  
  demoOneNavVc.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"One" image:nil selectedImage:nil];
  demoTwoNavVc.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Two" image:nil selectedImage:nil];
  demoInfoNavVc.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Info" image:nil selectedImage:nil];
  licensedDebugSettingNavVc.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Debug" image:nil selectedImage:nil];
  tabbarController.viewControllers = @[demoOneNavVc, demoTwoNavVc, demoInfoNavVc, licensedDebugSettingNavVc];
  
  self.window.rootViewController = tabbarController;
}


@end
